package ru.ifmo.croak.impl.replication.team1;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ru.ifmo.croak.api.Message;
import ru.ifmo.croak.impl.replication.team1.io.MulticastMessage;
import ru.ifmo.croak.impl.replication.team1.io.MulticastMessage.MessageType;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.MulticastSocket;
import java.util.List;

class Receiver implements Runnable {
    private static final int BUFFER_SIZE = 65536;
    private static final Logger log = LoggerFactory.getLogger(Receiver.class);

    private final MulticastSocket receivingSocket;
    private final LocalContext localContext;

    public Receiver(MulticastSocket receivingSocket, LocalContext localContext) {
        this.receivingSocket = receivingSocket;
        this.localContext = localContext;
    }

    public void run() {
        try {
            byte[] buf = new byte[BUFFER_SIZE];
            DatagramPacket pack = new DatagramPacket(buf, buf.length);
            while (!Thread.interrupted()) {
                receivingSocket.receive(pack);
                MulticastMessage multicastMessage = new MulticastMessage(pack);
                log.debug("message source UUID =  {} localContext UUID = {}", multicastMessage.getSourceUUID(), localContext.getLocalUUID());
                if (multicastMessage.getSourceUUID().equals(localContext.getLocalUUID())) {
                    log.debug("Got local message: {} {}", pack.getAddress(), multicastMessage.getMessageType());
                    continue;
                }
                localContext.addAliveHost(multicastMessage.getTcpSocketAddress());
                if (multicastMessage.getMessageType() == MessageType.NORMAL) {
                    Message message = multicastMessage.getMessage();
                    addMessage(message);
                    log.debug("Received message from: {}: {}", pack.getAddress(), message);
                } else if (multicastMessage.getMessageType() == MessageType.UPDATE_REQUEST) {
                    log.debug("broadcast update request from {}", pack.getAddress());
                    localContext.getSynchronizerManager().synchronizeTo(multicastMessage.getTcpSocketAddress());
                } else if (multicastMessage.getMessageType() == MessageType.PING) {
                    log.debug("broadcast ping from {}", pack.getAddress());
                } else {
                    log.warn("unknown message: {}", pack);
                }
            }
        } catch (IOException e) {
            log.error("IOException  ", e);
        }
    }

    private void addMessage(Message message) {
        boolean added = localContext.putMessageIntoLocalTimeOfArrivalAndUpdateXor(message.getId());
        if (added) {
            localContext.getPostService().postMessage(message);
        }
    }
}
